package gredis

import (
	"encoding/json"
	"mall-api-gin/pkg/logging"
	"time"

	"github.com/gomodule/redigo/redis"

	"mall-api-gin/pkg/setting"
)

var RedisConn *redis.Pool

// redis
const (
	healthCheckPeriod = time.Second * 30
)

// GetPools 集群所有的redis链接，
// 用途 redis 分布式锁
func GetPools() []*redis.Pool {
	return []*redis.Pool{RedisConn}
}

// Setup Initialize the Redis instance
func Setup() {
	var dialOpts []redis.DialOption
	//if setting.RedisSetting.User != "" {
	//	dialOpts = append(dialOpts, redis.DialUsername(setting.RedisSetting.User))
	//}
	if setting.RedisSetting.Password != "" {
		dialOpts = append(dialOpts, redis.DialPassword(setting.RedisSetting.Password))
	}
	if setting.RedisSetting.DbName > 0 {
		dialOpts = append(dialOpts, redis.DialDatabase(setting.RedisSetting.DbName))
	}

	RedisConn = &redis.Pool{
		MaxIdle:     setting.RedisSetting.MaxIdle,
		MaxActive:   setting.RedisSetting.MaxActive,
		Wait:        setting.RedisSetting.MaxActiveWait,
		IdleTimeout: setting.RedisSetting.IdleTimeout,
		Dial: func() (redis.Conn, error) {
			return redis.Dial("tcp", setting.RedisSetting.Host, dialOpts...)
		},
		TestOnBorrow: func(c redis.Conn, t time.Time) error {
			_, err := c.Do("PING")
			return err
		},
	}

	// PING
	if err := Ping(); err != nil {
		logging.Fatal("gredis.Setup err: %v", err)
	}
}

// Ping .
func Ping() (err error) {
	// PING
	conn := RedisConn.Get()
	defer conn.Close()
	_, err = conn.Do("PING")
	if err != nil {
		return
	}
	return
}

// Set a key/value
func Set(key string, data interface{}, time int) error {
	conn := RedisConn.Get()
	defer conn.Close()

	value, err := json.Marshal(data)
	if err != nil {
		return err
	}

	_, err = conn.Do("SET", key, value)
	if err != nil {
		return err
	}

	_, err = conn.Do("EXPIRE", key, time)
	if err != nil {
		return err
	}

	return nil
}

// Exists check a key
func Exists(key string) bool {
	conn := RedisConn.Get()
	defer conn.Close()

	exists, err := redis.Bool(conn.Do("EXISTS", key))
	if err != nil {
		return false
	}

	return exists
}

// Get get a key
func Get(key string) ([]byte, error) {
	conn := RedisConn.Get()
	defer conn.Close()

	reply, err := redis.Bytes(conn.Do("GET", key))
	if err != nil {
		return nil, err
	}
	return reply, nil
}

// Delete delete a kye
func Delete(key string) (bool, error) {
	conn := RedisConn.Get()
	defer conn.Close()

	return redis.Bool(conn.Do("DEL", key))
}

// LikeDeletes batch delete
func LikeDeletes(key string) error {
	conn := RedisConn.Get()
	defer conn.Close()

	keys, err := redis.Strings(conn.Do("KEYS", "*"+key+"*"))
	if err != nil {
		return err
	}
	for _, key := range keys {
		_, err = Delete(key)
		if err != nil {
			return err
		}
	}
	return nil
}

func Incr(key string, time int) (times int64, err error) {
	conn := RedisConn.Get()
	defer conn.Close()
	times, err = redis.Int64(conn.Do("INCR", key))
	if err != nil {
		logging.Error("INCR failed:", err)
		return
	}
	if times == 1 {
		_, err = conn.Do("EXPIRE", key, time)
		if err != nil {
			logging.Error("INCR failed:", err)
			return
		}
	}
	return
}

// ExistsAndExpire check a key
func ExistsAndExpire(key string) (exists bool, err error) {
	conn := RedisConn.Get()
	defer conn.Close()

	exists, err = redis.Bool(conn.Do("EXISTS", key))
	if err != nil {
		return
	}
	if !exists {
		return
	}
	_, err = conn.Do("expire", key, 0)

	return
}
